﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using AG.Data;
using AG.MapEditor.Data;
using AGMapEditor.Controls;

namespace AGMapEditor.Controls
{
    public partial class AGEMapPanel : UserControl
    {
        float _originOffsetY;

        float TW = 64.0f;
        float TH = 47.0f;
        AGMap _map;
       
        Dictionary<byte, AGModel> _terrainModel;
        Dictionary<ushort, AGModel> _unitModelList;

        public bool IsShowMapGrid { get; set; }

        public AGMapDesignMode MapDesignMode { get; private set; }

        private List<IMapPanelObserver> _observerList;
        public AGMap Map { get { return _map; } }

        public AGMapObject SelectedObject { get; private set; }

        public AGEMapPanel()
        {
            InitializeComponent();

            IsShowMapGrid = true;
            MapDesignMode = AGMapDesignMode.EditTerrain;
            _terrainModel = new Dictionary<byte, AGModel>();
            _unitModelList = new Dictionary<ushort, AGModel>();

            _observerList = new List<IMapPanelObserver>();

            this.DoubleBuffered = true;
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);

            if (e.KeyCode == Keys.ControlKey)
            {
                _isFlagCtrlKeyDown = true;
            }
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);

            if (e.KeyCode == Keys.ControlKey)
            {
                _isFlagCtrlKeyDown = false;
            }
        }

        public void Attach(IMapPanelObserver observer)
        {
            _observerList.Add(observer);
        }

        private void RaiseState()
        {
            for (int index = 0; index < _observerList.Count; index++)
            {
                _observerList[index].UpdateDesignState();
            }
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            if (_map != null)
            {
                Point pos = MapPointToMapPos(e.Location);
                if (MapDesignMode == AGMapDesignMode.EditTerrain)
                {
                    AGMapTile tile = _map.GetTile(pos.X, pos.Y);
                    //tile.TileIndex = 1;
                    SetTerrain(tile);

                    Invalidate();
                }
                else if (MapDesignMode == AGMapDesignMode.SetObject)
                {
                    AGMapTile tile = _map.GetTile(pos.X, pos.Y);
                    this.SetObje2(tile);

                    Invalidate();
                }
                else if (MapDesignMode == AGMapDesignMode.EditObject && SelectedObject != null)
                {
                    //SelectedObject.PosRow = (ushort)pos.X;
                    //SelectedObject.PosCol = (ushort)pos.Y;
                    //SelectedObject = null;
                }
                else if (MapDesignMode == AGMapDesignMode.EditBlock)
                {
                    AGMapTile tile = _map.GetTile(pos.X, pos.Y);
                    this.SetBlock(tile);

                    Invalidate();
                }

                CurPos = new Point(pos.X, pos.Y);
            }

            base.OnMouseClick(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (_map != null)
            {
                Point pos = MapPointToMapPos(e.Location);
                CurPos = new Point(pos.X, pos.Y);

                if (MapDesignMode == AGMapDesignMode.EditObject)
                {
                    this.OnUpdate4EditObject();
                }

                RaiseState();
                Invalidate();
            }

            base.OnMouseMove(e);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            //base.OnPaint(e);

            // render
            if (_map == null)
            {
                return;
            }

            if (MapDesignMode == AGMapDesignMode.EditBlock)
            {
                OnRenderDesignBlockMode(e.Graphics, e);
            }
            else if (MapDesignMode == AGMapDesignMode.EditObject && SelectedObject != null)
            {
                OnRenderEditObjectMode(e.Graphics, e);
            }
            else if (MapDesignMode == AGMapDesignMode.SetObject)
            {
                OnRenderSetObjectMode(e.Graphics, e);
            }
            else
            {
                #region
                for (ushort rowIndex = 0; rowIndex < _map.Row; rowIndex++)
                {
                    for (ushort colIndex = 0; colIndex < _map.Col; colIndex++)
                    {
                        PointF pt = MapPosToMapPoint(rowIndex, colIndex);
                        //e.Graphics.DrawRectangle(Pens.Green, pt.X, pt.Y, TW, TH);
                        pt.Y += _originOffsetY;

                        PointF pt2 = new PointF(pt.X, pt.Y - TH / 2);

                        Rectangle tileRect = new Rectangle((int)pt2.X, (int)pt2.Y, (int)TW, (int)TH);
                        if (e.ClipRectangle.IntersectsWith(tileRect))
                        {
                            AGMapTile tile = _map.GetTile(rowIndex, colIndex);
                            AGTexture texture = GetTileTexture(tile);
                            e.Graphics.DrawImage(texture.Bitmap, pt2.X, pt2.Y, texture.Size.Width, texture.Size.Height);

                            if (MapDesignMode == AGMapDesignMode.EditBlock)
                            {
                                if (tile.IsBlock)
                                {
                                    e.Graphics.DrawLine(Pens.Red, pt.X, pt.Y, pt.X + TW / 2, pt.Y - TH / 2);
                                    e.Graphics.DrawLine(Pens.Red, pt.X + TW / 2, pt.Y - TH / 2, pt.X + TW, pt.Y);
                                    e.Graphics.DrawLine(Pens.Red, pt.X + TW, pt.Y, pt.X + TW / 2, pt.Y + TH / 2);
                                    e.Graphics.DrawLine(Pens.Red, pt.X + TW / 2, pt.Y + TH / 2, pt.X, pt.Y);

                                    e.Graphics.DrawLine(Pens.Red, pt.X, pt.Y, pt.X + TW, pt.Y);
                                    e.Graphics.DrawLine(Pens.Red, pt.X + TW / 2, pt.Y - TH / 2, pt.X + TW / 2, pt.Y + TH / 2);
                                }
                                else
                                {
                                    e.Graphics.DrawLine(Pens.Green, pt.X, pt.Y, pt.X + TW / 2, pt.Y - TH / 2);
                                    e.Graphics.DrawLine(Pens.Green, pt.X + TW / 2, pt.Y - TH / 2, pt.X + TW, pt.Y);
                                    e.Graphics.DrawLine(Pens.Green, pt.X + TW, pt.Y, pt.X + TW / 2, pt.Y + TH / 2);
                                    e.Graphics.DrawLine(Pens.Green, pt.X + TW / 2, pt.Y + TH / 2, pt.X, pt.Y);
                                }
                            }
                            else
                            {
                                if (IsShowMapGrid)
                                {
                                    e.Graphics.DrawLine(Pens.Green, pt.X, pt.Y, pt.X + TW / 2, pt.Y - TH / 2);
                                    e.Graphics.DrawLine(Pens.Green, pt.X + TW / 2, pt.Y - TH / 2, pt.X + TW, pt.Y);
                                    e.Graphics.DrawLine(Pens.Green, pt.X + TW, pt.Y, pt.X + TW / 2, pt.Y + TH / 2);
                                    e.Graphics.DrawLine(Pens.Green, pt.X + TW / 2, pt.Y + TH / 2, pt.X, pt.Y);
                                }
                            }
                        }
                    }
                }

                for (int index = 0; index < _map.ObjectList.Count; index++)
                {
                    AGMapObject mapObject = _map.ObjectList[index];
                    PointF tileCenterPoint = MapPosToUIPoint(mapObject.PosRow, mapObject.PosCol);
                    tileCenterPoint.X += TW / 2;

                    AGModel model = GetUnitModel(mapObject.UnitId);
                    AGModelFrame frame = model.GetAction(0).GetDirection(0).GetFrame(0);

                    Rectangle destRect = new Rectangle(
                        (int)tileCenterPoint.X - frame.Pivot.X,
                        (int)tileCenterPoint.Y - frame.Pivot.Y,
                        frame.SrcRect.Width,
                        frame.SrcRect.Height);

                    if (e.ClipRectangle.IntersectsWith(destRect))
                    {
                        AGTexture texture = AGMEContext.Current.Resource.GetTexture(frame.Texture);
                        e.Graphics.DrawImage(texture.Bitmap,
                            tileCenterPoint.X - frame.Pivot.X,
                            tileCenterPoint.Y - frame.Pivot.Y,
                            texture.Size.Width,
                            texture.Size.Height);
                        if (this.SelectedObject == mapObject)
                        {
                            e.Graphics.DrawRectangle(Pens.Red, destRect);
                        }
                    }
                }
                #endregion
            }

            //e.Graphics.DrawImage(_memoryBitmap, e.ClipRectangle, e.ClipRectangle, GraphicsUnit.Pixel);
        }

        #region tool method

        private AGTexture GetTileTexture(AGMapTile tile)
        {
            AGModel model = GetTerrainModel(tile.TerrainId);
            AGModelFrame frame = model.GetAction(tile.TileId).GetDirection(0).GetFrame(tile.TileIndex);
            return AGMEContext.Current.Resource.GetTexture(frame.Texture);
        }

        private AGModel GetTerrainModel(byte terrainId)
        {
            if (_terrainModel.ContainsKey(terrainId))
            {
                return _terrainModel[terrainId];
            }
            
            AGTerrain terrain = AGMEContext.Current.Data.GetTerrain(terrainId);
            AGModel model = AGMEContext.Current.Resource.GetModel(terrain.ModelId);
            _terrainModel.Add(terrain.Id, model);
            return model;
        }

        private AGModel GetUnitModel(ushort unitId)
        {
            if (_unitModelList.ContainsKey(unitId))
            {
                return _unitModelList[unitId];
            }
            AGUnit unit = null;
            for (int index = 0; index < AGMEContext.Current.Data.UnitList.Count; index++)
            {
                if (AGMEContext.Current.Data.UnitList[index].Id == unitId)
                {
                    unit = AGMEContext.Current.Data.UnitList[index];
                    break;
                }
            }

            if (unit != null)
            {
                AGModel model = AGMEContext.Current.Resource.GetModel(unit.ModelId);
                _unitModelList.Add(unitId, model);
                return model;
            }
            return null;
        }

        #endregion
    }
}
